import { _decorator, Component, Vec3 } from 'cc';
import { Grid, GridNode } from './Grid';

const { ccclass, property } = _decorator;

/**
 * 寻路系统
 * 
 * 功能：
 * 1. 计算最短路径
 * 2. 处理动态障碍物
 * 3. 优化路径平滑度
 * 4. 管理寻路网格
 * 
 * 特点：
 * 1. A*算法实现
 * 2. 动态路径更新
 * 3. 多线程支持
 * 4. 缓存优化
 */
@ccclass('Pathfinder')
export class Pathfinder extends Component {
    @property(Grid)
    private grid: Grid | null = null;

    public findPath(startPos: Vec3, targetPos: Vec3): Vec3[] {
        const startNode = this.grid?.getNodeFromWorldPoint(startPos);
        const targetNode = this.grid?.getNodeFromWorldPoint(targetPos);

        if (!this.grid || !startNode || !targetNode) {
            return [];
        }

        const openSet: GridNode[] = [];
        const closedSet: Set<GridNode> = new Set();
        openSet.push(startNode);

        while (openSet.length > 0) {
            let currentNode = openSet[0];
            for (let i = 1; i < openSet.length; i++) {
                if (this.getFCost(openSet[i]) < this.getFCost(currentNode) ||
                    this.getFCost(openSet[i]) === this.getFCost(currentNode) &&
                    openSet[i].hCost < currentNode.hCost) {
                    currentNode = openSet[i];
                }
            }

            openSet.splice(openSet.indexOf(currentNode), 1);
            closedSet.add(currentNode);

            if (currentNode === targetNode) {
                return this.retracePath(startNode, targetNode);
            }

            const neighbors = this.grid.getNeighbors(currentNode);
            for (const neighbor of neighbors) {
                if (!neighbor.walkable || closedSet.has(neighbor)) {
                    continue;
                }

                const newMovementCostToNeighbor = currentNode.gCost + 
                    this.getDistance(currentNode, neighbor);
                
                if (newMovementCostToNeighbor < neighbor.gCost || 
                    !openSet.includes(neighbor)) {
                    neighbor.gCost = newMovementCostToNeighbor;
                    neighbor.hCost = this.getDistance(neighbor, targetNode);
                    neighbor.parent = currentNode;

                    if (!openSet.includes(neighbor)) {
                        openSet.push(neighbor);
                    }
                }
            }
        }

        return [];
    }

    private getFCost(node: GridNode): number {
        return node.gCost + node.hCost;
    }

    private getDistance(nodeA: GridNode, nodeB: GridNode): number {
        const distX = Math.abs(nodeA.x - nodeB.x);
        const distY = Math.abs(nodeA.y - nodeB.y);

        if (distX > distY) {
            return 14 * distY + 10 * (distX - distY);
        }
        return 14 * distX + 10 * (distY - distX);
    }

    private retracePath(startNode: GridNode, endNode: GridNode): Vec3[] {
        const path: Vec3[] = [];
        let currentNode: GridNode | null = endNode;

        while (currentNode !== startNode) {
            path.push(this.grid!.getWorldPosition(currentNode));
            currentNode = currentNode.parent;
            if (!currentNode) break;
        }

        path.reverse();
        return this.simplifyPath(path);
    }

    private simplifyPath(path: Vec3[]): Vec3[] {
        const simplifiedPath: Vec3[] = [];
        let oldDirection = new Vec3();

        for (let i = 1; i < path.length; i++) {
            const newDirection = new Vec3();
            Vec3.subtract(newDirection, path[i], path[i - 1]);
            newDirection.normalize();

            if (!oldDirection.equals(newDirection)) {
                simplifiedPath.push(path[i - 1]);
            }

            oldDirection = newDirection;
        }

        if (path.length > 0) {
            simplifiedPath.push(path[path.length - 1]);
        }

        return simplifiedPath;
    }
} 